/*=============================================================================
*
*   Copyright (C) 2020 All rights reserved.
*
*   Filename:		boundary.hpp
*
*   Author: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Date: 2020-02-20 01:12
*
*   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Last modified:	2020-03-23 23:48
*
*   Description:
*
=============================================================================*/
#ifndef IBOUNDARY_H
#define IBOUNDARY_H

#include "../types.hpp"
#include "dof.hpp"
#include "time_phase.hpp"
#include <boost/property_tree/ptree.hpp>
#include <utility>

namespace geoxfem {
namespace model {

struct dirichlet {
  static inline std::string name = "Dirichlet";
  static inline bcType      type = bcType::dirichlet;
};
struct neumann {
  static inline std::string name = "Neumann";
  static inline bcType      type = bcType::neumann;
};

template <typename BoundaryType>
struct Boundary {

  using ptree        = boost::property_tree::ptree;
  using TimeFunc     = arithmetic::Time_Function_Interface;
  using BCNodeList   = std::unordered_map<Dof*, std::unique_ptr<TimeFunc>>;
  using BoundaryNode = std::pair<Dof*, std::unique_ptr<TimeFunc>>;

  Boundary()                = default;
  Boundary(const Boundary&) = default;
  Boundary(Boundary&&)      = default;
  ~Boundary()               = default;
  Boundary& operator=(const Boundary&) = default;
  Boundary& operator=(Boundary&&) = default;
  /*misc{{{*/
  /**
   * @brief get Class Name
   *
   * @return
   */
  std::string getClassName() const {
    return "Boundary";
  }
  /**
   * @brief clear
   */
  void clear() {
    m_listBC.clear();
  }
  /*}}}*/
  /*direct member access{{{*/

  ptree getData() const {
    GEOXFEM_NOT_IMPLEMENTED();
  }
  void setPhase(const TimePhase* pPhase) {
    m_pPhase = pPhase;
  }
  TimePhase* getPhase() const {
    return m_pPhase;
  }
  void append(Dof*                                            dof,
              std::unique_ptr<arithmetic::Time_Function_Interface>& spFunc) {
    m_listBC.emplace(dof, std::move(spFunc));
  }
  TimeFunc* getTimeFunction(Dof* dof) const {
    return m_listBC.at(dof).get();
  }
  void updateAt(const _real&) const {
    GEOXFEM_NOT_IMPLEMENTED();
  }
  /*}}}*/
  /*iterato access{{{*/

  BCNodeList::iterator getBCNodeBegin() {
    return m_listBC.begin();
  }
  BCNodeList::iterator getBCNodeEnd() {
    return m_listBC.end();
  }
  BCNodeList::const_iterator getBCNodeBegin() const {
    return m_listBC.begin();
  }
  BCNodeList::const_iterator getBCNodeEnd() const {
    return m_listBC.end();
  }
  /*}}}*/
  /* friend std::ostream& operator<<(std::ostream&                 os, */
  /*                                 const Boundary<BoundaryType>& boundary) {
   */
  /*   os << "Boundary" << BoundaryType::name; */
  /*   return os; */
  /* } */
  /* friend std::ostream& operator<<(std::ostream& os, const BoundaryNode&
   * bcNode); */

private:
  BCNodeList       m_listBC;
  const TimePhase* m_pPhase;
};

/* std::ostream& operator<<( */
/*   std::ostream&                                                os, */
/*   const std::pair<Dof*, std::unique_ptr<arithmetic::ITimeFunction>>& bcNode) { */
/*   return os; */
/* } */

template <>
void Boundary<dirichlet>::updateAt(const _real& ctime) const { /*{{{*/
  for(auto& bc : m_listBC)
    bc.first->setDirichletValue(bc.second->getValueAt(ctime));
  return;
} /*}}}*/
template <>
void Boundary<neumann>::updateAt(const _real& ctime) const { /*{{{*/
  for(auto& bc : m_listBC)
    bc.first->setNeumannValue(bc.second->getValueAt(ctime));
  return;
} /*}}}*/
template <>
std::string Boundary<dirichlet>::getClassName() const { /*{{{*/
  return "Dirichlet Boundary";
} /*}}}*/
template <>
std::string Boundary<neumann>::getClassName() const { /*{{{*/
  return "Neumann Boundary";
} /*}}}*/
template <>
boost::property_tree::ptree Boundary<dirichlet>::getData() const { /*{{{*/
  return m_pPhase->getData()
    .get_child("boundary_condition")
    .get_child("Dirichlet");
} /*}}}*/
template <>
boost::property_tree::ptree Boundary<neumann>::getData() const { /*{{{*/
  return m_pPhase->getData()
    .get_child("boundary_condition")
    .get_child("Neumann");
} /*}}}*/

}  // namespace model
}  // namespace geoxfem

#endif /* IBOUNDARY_H */
